class Snake{
     //    获取蛇的元素
    shake:HTMLElement;
    // 获取蛇头
    head:HTMLElement;
    // 获取蛇身   HTMLCollection 是一个类似数组的对象，里面存放着元素,表示html元素的集合，getElementsByTagName() 返回的就是 HTMLCollection对象
    bodies:HTMLCollectionOf<HTMLElement>; 
    constructor(){
        this.shake = document.querySelector("#snake") as HTMLElement;
        this.head =document.querySelector("#snake>div")!;
        this.bodies = document.getElementById("snake")!.getElementsByTagName('div');
    }

    // 获取蛇头的x坐标
    get X(){
        return this.head.offsetLeft
    }
    // 获取蛇头的Y坐标
    get Y(){
        return this.head.offsetTop
    }

    // 设置蛇头的x坐标
    set X(value:number){
        if(this.X==value) return; //当传入的值和当前值相等，则不执行，因为设置蛇头新坐标时，是X,Y坐标一起设置的，但每次控制只设置了一个，另外的不需要再设置，所以判断一下

        if(value<0 || value>290){
            throw new Error('x坐标超出范围')
        }
        // 禁止蛇掉头
        if(this.bodies[1] && this.bodies[1].offsetLeft===value){ //当即将设置的蛇头坐标和第一个蛇身x坐标位置相同时,表示蛇要在X轴掉头
            if(this.X>value){ //当前蛇头坐标大于即将设置的蛇头坐标，往左掉头，需要还原蛇头x的方向，继续往右走，所以在原有蛇头上+10
               value=this.X+10
            }else{
                value=this.X-10
            }
        }
        // 移动身体
        this.moveBody()
        this.head.style.left=value+'px' //移动蛇头
        this.checkSelf() //移动蛇头之后再检查是否碰到自己了
    }
    // 设置蛇头的Y坐标
    set Y(value:number){
        if(this.Y==value) return;
        if(value<0 || value>290){
            throw new Error('y坐标超出范围')
        }

        // 禁止蛇掉头
        if(this.bodies[1] && this.bodies[1].offsetTop===value){
            
            if(this.Y>value){ //当前蛇头坐标大于即将设置的蛇头坐标，往下掉头，需要还原蛇头x的方向，继续往上走，所以在原有蛇头上+10
                value=this.Y+10
             }else{
                 value=this.Y-10
             }
        }

        // 移动身体
        this.moveBody()
        this.head.style.top=value+'px' //移动蛇头
        this.checkSelf() //移动蛇头之后再检查是否碰到自己了
    }

    // 增加蛇的身体
    addBody(){
        // 向蛇的内部插入一个div
        this.shake.insertAdjacentElement('beforeend',document.createElement('div')) 
        // insertAdjacentElement(位置，要插入的子元素) 将元素插入到指定位置 位置有beforebegin（元素前）  afterend（元素后） afterbegin（第一个子元素） beforeend（最后一个子元素） beforebegin和afterend表示插入的是兄弟元素 afterbegin和beforeend只能在父元素上使用，插入的元素是被插入元素的子元素
    }
    // 蛇身移动
    moveBody(){
        /**
         * 获取蛇身，循环遍历，从后往前，给后面一个蛇身设置前一个蛇身的坐标
         */
        let bd =this.bodies;

        for(let i=bd.length-1;i>0;i--){
            bd[i].style.left=bd[i-1].offsetLeft+'px';
            bd[i].style.top=bd[i-1].offsetTop+'px';
        }
    }
    // 检查蛇是否碰到自己
    checkSelf(){
        let bd =this.bodies;
        for(let i=1;i<bd.length;i++){ //i=1，因为第一个蛇头是body[0],所以从第二个开始循环
            if(bd[i].offsetLeft==this.X && bd[i].offsetTop==this.Y){   //当蛇头和蛇身坐标相同时，蛇碰到自己了，游戏结束
                throw new Error('蛇碰到自己了')
            }
        }
    }
}

export default Snake;